#include "04_Acceptor.hpp"
#include "06_EventLoop.hpp"
#include "unistd.h"
#include "stdlib.h"

// 无论在哪里调用info_msg，都会显示的是函数执行处的行号，写了也白写，还是要替换为define
#if 0
inline void info_msg(const std::string& msg)
{
    printf("%s | %d | %s | %s \n", __FILE__, __LINE__, __FUNCTION__, msg.c_str());
}
#endif

// 宏函数的"\"后面必须直接跟着换行，不能有空格
#define info_msg(msg) do { \
    printf("%s | %d | %s | %s \n", __FILE__, __LINE__, __FUNCTION__, msg); \
} while(0)

#define error_msg(msg) do { \
    char buff[256] = {0};\
    snprintf(buff, sizeof(buff), "%s | %d | %s : %s", __FILE__, __LINE__, __FUNCTION__, msg);\
    perror(buff);\
} while(0)

namespace xy
{
Event_Loop::Event_Loop(Acceptor& acceptor)
    :_epfd(create_epollfd())
    ,_acceptor(acceptor)
    ,_evt_vec()         // 此处不能使用无参，否则epoll_wait没办法找到首地址
    ,_is_looping(false)
{
    _evt_vec.reserve(1024);
    add_epoll_read_event(_acceptor.get_fd());    // 首先监听id
}

Event_Loop::~Event_Loop()
{
    close(_acceptor.get_fd());
    info_msg("close successful");
}

int Event_Loop::create_epollfd()
{
    int _epfd = epoll_create1(0);   // 0表示使用默认配置
    if(_epfd == -1)
    {
        error_msg("epoll creatr error");
        exit(EXIT_FAILURE);         // 需要包含<stdlib.h>
    }

    return _epfd;
}

void Event_Loop::add_epoll_read_event(int fd)
{
    struct epoll_event evt;
    evt.data.fd = fd;
    evt.events = EPOLLIN;   // 监听读事件

    int ret = epoll_ctl(_epfd, EPOLL_CTL_ADD, fd, &evt);
    if(ret == -1)
    {
        error_msg("epoll_ctl add error");
        exit(EXIT_FAILURE);
    }
}

void Event_Loop::del_epoll_read_event(int fd)
{
    struct epoll_event evt;
    evt.data.fd = fd;
    evt.events = EPOLLIN;   // 监听读事件

    int ret = epoll_ctl(_epfd, EPOLL_CTL_DEL, fd, &evt);
    if(ret == -1)
    {
        error_msg("epoll_ctl del error");
        exit(EXIT_FAILURE);
    }

}

void Event_Loop::loop()
{
    // createfd, addfd与readfd都在构造函数里写好了，所以这里不需要loop负责了
    _is_looping = true;
    while(_is_looping)
    {
        wait_epollfd();     // 等待时间发生
    }
}

void Event_Loop::unloop()
{
    _is_looping = false;
}

void Event_Loop::wait_epollfd()
{
    // 1. 调用epoll_event等待事件就绪事件
    const int MAX_EVENTS = 1024;
    struct epoll_event events[MAX_EVENTS];

    // 2. 等待就绪的事件
    // - -1表示无限等待，但是退出时会卡住
    // - 考虑退出的话，这里改为1000
    //int nfds = epoll_wait(_epfd, events, MAX_EVENTS, -1);
    int nfds = epoll_wait(_epfd, events, MAX_EVENTS, 1000);
    if(nfds == -1)
    {
        if(errno == EINTR)
        {
            return;     // 被信号中断不退出，直接返回, loop会继续循环
        }
        else
        {
            error_msg("epoll wait error");
            exit(EXIT_FAILURE);
        }
    }
    else if(nfds == 0)
    {
        return;         // 超时，继续等待
    }
    
    // 3. 遍历就绪的时间
    for (int i = 0; i < nfds; ++i)
    {
        int fd = events[i].data.fd;
        uint32_t event = events[i].events;

        // 3.1 如果事件是监听套接字上的连接事件，调用acceptor的accept方法接受连接，并将新连接，并将新连接添加到epoll中
        if(fd == _acceptor.get_fd() && (event & EPOLLIN))
        {
            // 下面这句报错，可能需要一个构造函数
            //TCPconnection conn = _acceptor.accept();
            
            int connfd = _acceptor.accept();
            TCPconnection::TCPconnectionPtr conn(new TCPconnection(connfd, this));
            _conns[connfd] = conn;

            // 设置回调
            conn->set_connection_callback(_on_connection);
            conn->set_message_callback(_on_message);
            conn->set_close_callback(_on_close);

            // 触发调用
            add_epoll_read_event(connfd);
            conn->handle_connection_callback();

            #if 0   // 走回调路线，这些都不要了
            if(conn.is_closed() != true)
            {
                // 保存连接
                _conns[conn.get_fd()] = conn;
                // 向epoll添加读事件
                add_epoll_read_event(conn.get_fd());
                // 触发回调
                if(_on_connection)
                {
                    _on_connection(conn);
                }
            }
            #endif
        }

        // 3.2 如果事件是读事件，从_conns中获取对应的TCPconnection对象，并调用handle_read方法
        else if(event & EPOLLIN)
        {
            auto it = _conns.find(fd);
            if(it != _conns.end())
            {
                TCPconnection::TCPconnectionPtr& conn = it->second;
                
                // 处理读事件
                //int ret = conn.handle_message_callback();
                int nread = conn->receive(); 
                // 有数据可读，处理消息
                if(nread > 0)
                {
                    conn->handle_message_callback();
                }
                // 连接关闭或发生错误
                else if(nread == 0 || nread == -1)
                {
                    conn->handle_close_callback();
                    _conns.erase(it);
                    
                    #if 0   // 改为回调
                    // 连接关闭
                    if(_on_close)
                    {
                        _on_close(conn);
                    }
                    // 从epoll中移除
                    del_epoll_read_event(conn.get_fd());
                    _conns.erase(it);
                    #endif
                }
            }
            else
            {
                // 未找到对应的连接，可能已被关闭
                error_msg("handle_read error");
                // 从epoll中移除
                del_epoll_read_event(fd);
                //_conns.erase(it);
            }
        }

        // 3.3 处理可能的错误

    }
}

// 3: 注册回调函数(回调函数的接口) | 存储传递进来的回调函数，就像服务员的点餐记录本，暂时保存顾客点的菜
void Event_Loop::set_connection_callback(TCP_Connection_Callback cb)
{
    _on_connection = cb;
}

void Event_Loop::set_message_callback(TCP_Message_Callback cb)
{
    _on_message = cb;
}

void Event_Loop::set_close_callback(TCP_Close_Callback cb)
{
    _on_close = cb;
}

}
